package controllers

import (
	"container/list"
	"github.com/prometheus/common/log"
	"github.com/shopspring/decimal"
	"matching/enum"
	"matching/errcode"
	"matching/middleware/cache"
	"matching/model"
	"matching/process"
	"matching/utils"
	"math/rand"
	"strconv"
	"time"
)

type OrderController struct {
	BaseController
}

func (m *OrderController) CreateOrderOne() {
	action:=m.GetString("action")
	symbol:=m.GetString("symbol")
	orderId:=m.GetString("orderId")
	side:=m.GetString("side")
	Type:=m.GetString("type")
	amount:=m.GetString("amount")
	price:=m.GetString("price")
	userId:=m.GetString("userId")
	triggerPrice:=m.GetString("triggerPrice")

	if !m.paramsCheck(action,symbol,orderId,side,Type,amount,price,userId,price,triggerPrice) {
		m.resultError("params not be null")
		return
	}

	// check side action type
	if side!="BUY" && side!="SELL" {
		m.resultError(" side just BUY or SELL")
		return
	}
	if action!="create" && side!="cancel" {
		m.resultError(" action just create or cancel")
		return
	}
	if Type!="Limit" && Type!="LimitIoc" && Type!="Market" && Type!="MarketTop5" && Type!="MarketTop10" {
		m.resultError(" type just Limit、LimitIoc、Market、MarketTop5 or MarketTop10")
		return
	}

	if utils.OrderTypeStringToEnum(Type)==enum.Limit || utils.OrderTypeStringToEnum(Type)==enum.LimitIoc {
		if utils.StringToDecimal(price).LessThanOrEqual(decimal.Zero) {
			m.resultError("limit price can not be zero or less than zero")
			return
		}
	}

	order:=model.Order{
		Action:       utils.ActionStringToEnum(action),
		Symbol:       symbol,
		OrderId:      orderId,
		Side:         utils.SideStringToEnum(side),
		Type:         utils.OrderTypeStringToEnum(Type),
		Amount:       utils.StringToDecimal(amount),
		TradedAmount: decimal.Zero,
		Price:        utils.StringToDecimal(price),
		Timestamp:    time.Now().UnixNano() / 1e3,
		UserId:       userId,
		Turnover:     decimal.Zero,
		Status:       enum.TREADING,
		TriggerPrice: utils.StringToDecimal(triggerPrice),
	}
	err:=process.Dispatch(order)
	if err==errcode.Success{
		m.resultOk()
	}else if err==errcode.OrderExist{
		m.resultError("orderId must be unique")
	}else if err==errcode.EngineNotFound{
		m.resultError("engine not start")
	}else {
		m.resultError("unknown error")
	}
}



// 测试撮合引擎性能，模拟真实环境，在接口处执行
func (m *OrderController) CreateOrderTest() {
	priceMin:=54500.12
	priceMax:=54600.33
	orders:=list.New()
	for i:=0;i<100000;i++{
		price:=randFloats(priceMin,priceMax)
		var amount float64

		var side enum.OrderSide
		if i%2==0 {
			//buy
			side=enum.SideBuy
		}else{
			//sell
			side=enum.SideSell
		}

		symbol:="BTC/USDT"
		if i%3==0 {
			symbol="ETH/USDT"
		}else if i%5==0 {
			symbol="EOS/USDT"
		}else if i%7==0 {
			symbol="SOL/USDT"
		}else if i%11==0 {
			symbol="ADA/USDT"
		}


		var Type enum.OrderType
		var amd decimal.Decimal
		if i%7==0 {
			Type=enum.Market
			if side==enum.SideBuy {
				amount=randFloats(100,500)
				amd=decimal.NewFromFloat(amount).Round(2)
			}else {
				amount=randFloats(0.01,0.2)
				amd=decimal.NewFromFloat(amount).Round(5)
			}
		}else{
			Type=enum.Limit
			amount=randFloats(0.01,0.2)
			amd=decimal.NewFromFloat(amount).Round(5)
		}
		if symbol==""{

		}
		order:=model.Order{
			Action:       enum.ActionCreate,
			Symbol:       symbol,
			OrderId:      "OXT00"+ strconv.Itoa(i),
			Side:         side,
			Type:         Type,
			Amount:       amd,
			TradedAmount: decimal.Zero,
			Price:        decimal.NewFromFloat(price).Round(2),
			Timestamp:    time.Now().UnixNano() / 1e3,
			UserId:       "user1",
			Turnover:     decimal.Zero,
			Status:       enum.TREADING,
			TriggerPrice: utils.StringToDecimal("0"),
		}
		orders.PushBack(order)

	}
	start:=time.Now()
	cache.SaveTime(strconv.FormatInt(start.Unix(),10))
	log.Infof("订单数量:%d,开启撮合时间:%s",orders.Len(),start.String())
	for e := orders.Front(); e != nil; e = e.Next() {
		process.DispatchTest(e.Value.(model.Order))
	}
	end:=time.Now()
	log.Infof("耗时:%d",end.Unix()-start.Unix())
	m.resultOk()

}

func randFloats(min, max float64) float64 {
	number:=min + rand.Float64() * (max - min)
	return number
}
